using System;
using System.Text;

namespace InfiniTec.Expressions
{
    public class LdapFormatter: IExpressionFormatter
    {
        #region IExpressionFormatter Members

        public string FormatExpression(Expression expression)
        {
            StringBuilder sb;

            sb = new StringBuilder();

            FormatExpressionInternal(expression, sb);

            return sb.ToString();
        }

        private static void FormatExpressionInternal(Expression expression, StringBuilder sb)
        {
            OperatorExpression operatorExpression;

            if (expression is NullExpression) return;

            operatorExpression = expression as OperatorExpression;

            if (operatorExpression != null)
            {
                sb.Append("(");
                if (operatorExpression is AndExpression) sb.Append("&");
                else if (operatorExpression is OrExpression) sb.Append("|");
                else if (operatorExpression is NotExpression) sb.Append("!");

                foreach (Expression childExpression in operatorExpression.Expressions)
                {
                    FormatExpressionInternal(childExpression, sb);
                }

                sb.Append(")");
            }
            else
            {
                if (FormatAs<string>(expression, sb)) return;
                if (FormatAs<int>(expression, sb)) return;
                if (FormatAs<byte[]>(expression, sb)) return;
                if (FormatAs<Guid>(expression, sb)) return;
                if (FormatAs<float>(expression, sb)) return;
                if (FormatAs<long>(expression, sb)) return;
                if (FormatAs<DateTime>(expression, sb)) return;
                if (FormatAs<double>(expression, sb)) return;
            }

        }

        private static bool FormatAs<T>(Expression expression, StringBuilder sb)
        {
            CompareExpression<T> compareExpression;

            compareExpression = expression as CompareExpression<T>;

            if (compareExpression == null) return false;

            sb.Append("(");

            if (compareExpression.CompareType == CompareType.NotEquals)
            {
                sb.Append("!");
            }
            sb.Append(compareExpression.PropertyDefinition.Name);
            switch (compareExpression.CompareType)
            {
                case CompareType.Equals:
                case CompareType.NotEquals: sb.Append("="); break;
                case CompareType.Greater: sb.Append(">"); break;
                case CompareType.GreaterOrEqual: sb.Append(">="); break;
                case CompareType.Smaller: sb.Append("<"); break;
                case CompareType.SmallerOrEqual: sb.Append("<="); break;
                default: break;
            }

            if (typeof(T) == typeof(byte[]))
            {
                sb.Append(ConvertByteArrayToOctetString((byte[])(object)compareExpression.Value));
            }
            else
            {
                sb.Append(compareExpression.Value.ToString());
            }

            sb.Append(")");
            return true;
        }

        private static string ConvertByteArrayToOctetString(byte[] array)
        {
            StringBuilder sb;

            if (array == null) return string.Empty;

            sb = new StringBuilder(array.Length*3);
            foreach (byte b in array)
            {
                sb.Append("x");
                sb.Append(b.ToString("X2"));
            }
            return sb.ToString();
            
        }

        #endregion
    }
}
